Shell学习笔记13-Shell数组

GO


1. Shell数组介绍

1.1. 为什么会产生Shell数组

通常在开发Shell脚本时,定义变量采用的形式为a=1;b=2;c=3,可如果有多个变量呢?这时再逐个地定义就会很费劲,并且要是有多个不确定的变量内容,也会难以进行变量定义,此外,快速读取不同变量的值也是一件很痛苦的事情,于是数组就诞生了,它就是为了解决上述问题而出现的。

1.2. 什么是Shell数组

简单地说,Shell的数组就是一个元素集合,它把有限个元素(变量或字符内容)用一个名字来命令,然后用编号对它们进行区分。这名字就称为数组名,用于区分不同内容的编号就称为数组下标。组成数组的各个元素(变量)称为数组的元素,有时也称为下标变量

有了Shell数组之后,就可以用相同名字来引用一系列变量及变量值了,并通过数字(索引)来识别使用它们。在很多场合中,使用数组可以缩短和简化程序开发。

2. Shell数组的定义与增删改查

2.1. Shell数组的定义

Shell数组的定义有多种方法,列举如下:

方法1

用小括号将变量值括起来赋值给数组变量,每个变量值之间要用空格进行分隔。

语法:array=(value1 value2 value3 ...)

示例如下:

1
2
3
[root@theshu ~]# array=(1 2 3) #<==数组定义
[root@theshu ~]# echo ${array[*]} #<==输出上面定义的数组的所有元素值,注意语法
1 2 3

方法2

用小括号将变量值括起来,同时采用键值对的形式赋值。

语法:array=([1]=one [2]=two [3]=three)

此种方法为key-value键值对的形式,小括号里对应的数字为数组下标,等号后面的内容为下标对应的数组变量的值,此方法比较复杂,不推荐使用。

示例如下:

1
2
3
4
5
6
7
8
9
[root@theshu ~]# array=([1]=one [2]=two [3]=three) #<==数组定义
[root@theshu ~]# echo ${array[*]} #<==输出上面定义的数组的所有元素值
one two three
[root@theshu ~]# echo ${array[1]} #<==输出上面定义的数组的第一个元素值
one
[root@theshu ~]# echo ${array[2]} #<==输出上面定义的数组的第二个元素值
two
[root@theshu ~]# echo ${array[3]} #<==输出上面定义的数组的第三个元素值
three

方法3

通过分别定义数组变量的方法来定义。

语法:array[0]=a; array[1]=b; array[2]=c

此种定义方法比较麻烦,不推荐使用。

示例如下:

1
2
3
4
5
6
[root@theshu ~]# array[0]=a
[root@theshu ~]# array[1]=b
[root@theshu ~]# array[2]=c
[root@theshu ~]# array[3]=d
[root@theshu ~]# echo ${array[*]}
a b c d

方法4

动态地定义数组变量,并使用命令的输出结果作为数组的内容。

语法:

1
2
3
array=($(命令))
array=(`命令`)

示例如下:

1
2
3
4
5
6
7
8
9
10
[root@theshu ~]# mkdir /array/ -p
[root@theshu ~]# touch /array/{1..3}.txt
[root@theshu ~]# ll /array/
total 0
-rw-r--r-- 1 root root 0 Mar 1 11:31 1.txt
-rw-r--r-- 1 root root 0 Mar 1 11:31 2.txt
-rw-r--r-- 1 root root 0 Mar 1 11:31 3.txt
[root@theshu ~]# array=($(ls /array))
[root@theshu ~]# echo ${array[*]}
1.txt 2.txt 3.txt

方法5

还可以使用declare -a array来定义数组类型,但是这样用的情况比较少。一般不用。了解即可。

2.2. Shell数组的打印及输出

1. 打印数组元素

此为常用知识点,需要重点掌握。

  • 打印单个数组元素用${数组名[下标]},当未指定数组小标时,数组的小标将从0开始。
  • 使用*@可以得到整个数组的内容。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
[root@theshu ~]# array=(one two three)
[root@theshu ~]# echo ${array[0]}
one
[root@theshu ~]# echo ${array[1]}
two
[root@theshu ~]# echo ${array[2]}
three
[root@theshu ~]# echo ${array[*]}
one two three
[root@theshu ~]# echo ${array[@]}
one two three

2. 打印数组元素的个数

此为常用知识点,需要重点掌握。

  • 使用${井数组名[@或*]}可以得到数组的长度。
  • 这和变量子串的知识是一样的,因为数组也是变量,只不过是特殊的变量,因此变量的子串替换等知识也适用于数组。

示例如下:

1
2
3
4
5
6
7
8
[root@theshu ~]# echo ${array[*]}
one two three
[root@theshu ~]# echo ${#array[*]}
3
[root@theshu ~]# echo ${array[@]}
one two three
[root@theshu ~]# echo ${#array[@]}
3

3. 数组赋值

此知识不常用,了解即可。

  • 可直接通过数组名[下标]对数组进行引用赋值,如果下标不存在,则自动添加一个新的数组元素,如果小标存在,则覆盖原来的值。

示例如下:

1
2
3
4
5
6
7
8
9
[root@theshu ~]# array=(one two three)
[root@theshu ~]# echo ${array[*]}
one two three
[root@theshu ~]# array[3]=four
[root@theshu ~]# echo ${array[*]}
one two three four
[root@theshu ~]# array[0]=theshu
[root@theshu ~]# echo ${array[*]}
theshu two three four

4. 数组的删除

因为数组本质上还是变量,因此可通过unset 数组[下标]清除相应的数组元素,如果不带下标,则表示清除整个数组的所有数据。

示例如下:

1
2
3
4
5
6
7
[root@theshu ~]# echo ${array[*]}
theshu two three four
[root@theshu ~]# unset array[1]
[root@theshu ~]# echo ${array[*]}
theshu three four
[root@theshu ~]# unset array
[root@theshu ~]# echo ${array[*]}

5. 数组内容的截取和替换

这里和变量子串的替换是一样的,因为数组是特殊的变量。

数组元素部分的内容截取的示例如下:

1
2
3
4
5
6
7
8
9
10
[root@theshu ~]# array=(1 2 3 4 5)
[root@theshu ~]# echo ${array[@]:1:3} #<==截取1号到3号数组元素
2 3 4
[root@theshu ~]# array=($(echo {a..z})) #<==将变量的结果赋值给数组变量
[root@theshu ~]# echo ${array[@]}
a b c d e f g h i j k l m n o p q r s t u v w x y z
[root@theshu ~]# echo ${array[@]:1:3} #<==截取下标为1到3的数组元素
b c d
[root@theshu ~]# echo ${array[@]:0:2} #<==截取下标为0到2的数组元素
a b

替换数组元素部分内容的代码如下:

1
2
3
4
[root@theshu ~]# array=(1 2 3 1 1)
[root@theshu ~]# echo ${array[@]/1/b}
b 2 3 b b
#<==把数组中的1替换成b,原数组未被修改,和sed很像

提示:调用方法为${数组名[@或*]/查找字符/替换字符},该操作不会改变原先数组的内容,如果需要修改,可以参考上面的例子,重新定义数组。

删除数组元素部分内容的代码如下:

1
2
3
4
5
6
7
8
9
10
11
[root@theshu ~]# array=(one two three four five)
[root@theshu ~]# echo ${array[@]}
one two three four five
[root@theshu ~]# echo ${array[@]#o*} #<==从左边开始匹配最短的数组元素,并删除
ne two three four five
[root@theshu ~]# echo ${array[@]##o*} #<==从左边开始匹配最长的数组元素,并删除
two three four five
[root@theshu ~]# echo ${array[@]%f*} #<==从右边开始匹配最短的数组元素,并删除
one two three
[root@theshu ~]# echo ${array[@]%%f*} #<==从右边开始匹配最长的数组元素,并删除
one two three

提示:数组也是变量,因此也适合于前面讲解过的变量的子串处理的功能应用。数组的其他相关知识可通过man bash命令然后搜索Areays来了解。

3. Shell数组脚本开发实践

范例1:使用循环批量输出数组的元素

  • 方法1:通过C语言型的for循环语句打印数组元素

    1
    2
    3
    4
    5
    6
    #!/bin/bash
    array=(1 2 3 4 5)
    for ((i=0; i<${#array[*]}; i++))
    do
    echo ${array[i]}
    done
  • 方法2:通过普通for循环语句打印数组元素

    1
    2
    3
    4
    5
    6
    #!/bin/bash
    array=(1 2 3 4 5)
    for n in ${array[*]}
    do
    echo $n
    done
  • 方法3:使用while循环语句打印数组元素

    1
    2
    3
    4
    5
    6
    7
    8
    #!/bin/bash
    array=(1 2 3 4 5)
    i=0
    while ((i<${#array[*]}))
    do
    echo ${array[i]}
    ((i++))
    done

范例2:通过竖向列举法定义数组元素并批量打印

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
array=(
theshu
theshan
thepi
thewang
)
for ((i=0; i<#{#array[*]}; i++))\
do
echo "This is num $i, then content is ${array[$i]}"
done
echo ------------------
echo "array len:${#array[*]}"

4. Shell数组的重要命令

4.1. 定义命令

  • 静态数组:array=(1 2 3)
  • 动态数组:array=($(ls))
  • 为数组赋值:array[3]=4

4.2. 打印命令

  • 打印所有元素:${array[@]}${array[*]}
  • 打印数组长度:${井array[@]}${井array[*]}
  • 打印单个元素:${array[i]}其中i是数组下标

4.3. 循环打印的常用基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#!/bin/bash
arr=(
10.0.0.11
10.0.0.22
10.0.0.33
)
#<==C语言型for循环语法
for ((i=0; i<${#arr[*]; i++}))
do
echo "${arr[$i]}"
done
echo ----------------
#<==普通for循环语法
for n in ${arr[*]}
do
echo "$n"
done

5. Shell数组相关面试题及高级实战案例

5.1. 范例1

利用bash for循环打印下面这句话中字母数不大于6的单词(某企业面试真题):I am theshu teacher welcome to theshu training class

解答思路具体如下:

  • 先把所有的单词放到数组里,然后一次进行判断。命令:array=(I am theshu teache welcome to theshu training class)
  • 计算变量内容的长度。常见的方法有4种:char=theshu

    • echo $char | wc -L
    • echo ${井char}
    • expr length $char
    • echo $char | awk '{print length($0)}'
  • 方法1:通过数组方法来实现(本例给出了用两种for循环打印数组元素的方法):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    arr=(I am theshu teacher welcome to theshu training class)
    for ((i=0; i<${#arr[*]; i++}))
    do
    if [ ${#arr[$i]} -lt 6 ]
    then
    echo "${arr[$i]}"
    fi
    done
    echo ------------
    for word in ${array[*]}
    do
    if [ `expr length $word` -lt 6 ];then
    echo $word
    fi
    done
  • 方法2:使用for循环列举取值列表法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    for word in I am theshu teacher welcome to trheshu training class
    do
    if [ `echo $word | wc -L` -lt 6 ]; then
    echo $word
    fi
    done
    chars="I am theshu teacher welcome to theshu training class"
    for word in $chars
    do
    if [ `echo $word | wc -L` -lt 6 ]; then
    echo $word
    fi
    done
  • 方法3:通过awk循环实现

    1
    2
    chars-"I am theshu teacher welcome ti theshu training class"
    echo $chars | awk '{for(i=1;i<NF;i++) if(length($i)<=6) print $i}'

5.2. 范例2

批量检查多个网站地址是否正常。要求如下:

  • 使用Shell数组的方法实现,检测策略尽量模拟用户访问
  • 每10秒进行一次全部检测,无法访问的输出报警
  • 待检测的地址如下:
    1
    2
    3
    4
    http://blog.theshu1.com
    http://blog.theshu2.com
    http://blog.theshu3.com
    http://10.0.0.7

解体思路:

  • 把URL定义成数组,形成函数
  • 编写URL检查脚本函数,传入数组的元素,即URL
  • 组合实现整个案例,编写main主函数(即执行函数),每隔10秒检查一次

下面的参考答案采用了Shell数组的方法,同时检测多个URL是否正常,并给出专业的展示效果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash
. /etc/init.d/functions
check_count=0
url_list=(
http://blog.theshu1.com
http://blog.theshu2.com
http://blog.theshu3.com
http://10.0.0.7
)
function wait(){
echo -n '3秒后,执行检查URL操作.';
for ((i=0; i<3; i++))
do
echo -n "."; sleep 1
done
done
echo
}
function check_url(){
wait
for ((i=0; i<`echo ${#url_list[*]}`; i++))
do
wget -o /dev/null -T 3 --tries=1 --spider ${url_list[$i]} >/dev/null 2>&1
if [ $? -eq 0 ]
then
action "${url_list[$i]}" /bin/true
else
action "${url_list[$i]}" /bin/false
fi
done
((check_count++))
}
main(){
while true
do
check_url
echo "------------check count:${check_count}----------"
sleep 10
done
}
main

提示:实际使用时,一些基础的函数脚本(例如:加颜色的函数)是放在函数文件里的(例如:放在/etc/init.d/functions里),与执行的脚本内容部分分离,这样看起来会更清爽,大型的语言程序都是这样开发的。


OK

0%